﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using IllusionUtility.GetUtility;
using Studio;
using UnityEngine;

namespace WzhAddon
{
	public class AutoTrackObject : MonoBehaviour
	{
		public ObjectCtrlInfo targetChara;
		public ObjectCtrlInfo sourceChara;
		public Transform sourceTransform;
		public Transform targetTransform;
		public Vector3 offset = Vector3.zero;
		public Quaternion offsetRot = Quaternion.identity;
		public bool trackEnabled;
		public bool adjusting;
		public bool trackAsRelative = true;
		public bool trackRoation = true;
		public bool updateBeforeAnimation;
		public bool updateBeforeNextFrame = AutoTrackObjectMgr.defaultUpdateBeforeNextFrame;
		private bool attachedAtFrame;
		public static AutoTrackObject Init(ObjectCtrlInfo sourceChara, Transform sourceTransform)
		{
			AutoTrackObject autoTrackObject = sourceTransform.gameObject.AddComponent<AutoTrackObject>();
			autoTrackObject.sourceChara = sourceChara;
			autoTrackObject.sourceTransform = sourceTransform;
			AutoTrackObjectController.Get(sourceChara).autoTrackObjects.Add(autoTrackObject);
			return autoTrackObject;
		}
		public static AutoTrackObject Get(GameObject go)
		{
			return go.GetComponent<AutoTrackObject>();
		}
		public void Detach()
		{
			this.targetChara = null;
			this.targetTransform = null;
			this.CalcOffset();
		}
		public void AttachTo(ObjectCtrlInfo targetChara, Transform targetTransform, bool immediate = false)
		{
			this.targetChara = targetChara;
			if (targetTransform == null && targetChara != null)
			{
				this.targetTransform = targetChara.guideObject.transformTarget;
			}
			else
			{
				this.targetTransform = targetTransform;
			}
			if (immediate)
			{
				this.CalcOffset();
				return;
			}
			this.adjusting = true;
			this.attachedAtFrame = true;
		}
		public bool AttachToBoneName(ObjectCtrlInfo targetChara, string name, bool immediate = false)
		{
			foreach (Transform transform in this.FindBonesFor(targetChara))
			{
				if (transform.name == name)
				{
					this.targetChara = targetChara;
					this.targetTransform = transform;
					if (immediate)
					{
						this.CalcOffset();
					}
					else
					{
						this.adjusting = true;
						this.attachedAtFrame = true;
					}
					return true;
				}
			}
			GameObject gameObject = targetChara.guideObject.transformTarget.gameObject;
			if (gameObject != null)
			{
				GameObject gameObject2 = TransformFindEx.FindLoop(gameObject.transform, name);
				if (gameObject2 != null)
				{
					this.targetChara = targetChara;
					this.targetTransform = gameObject2.transform;
					if (immediate)
					{
						this.CalcOffset();
					}
					else
					{
						this.adjusting = true;
						this.attachedAtFrame = true;
					}
					return true;
				}
			}
			return false;
		}
		public static List<ObjectCtrlInfo> GetObjectCtrlInfoListByNear(ObjectCtrlInfo from, bool includeSelf, bool visibleOnly)
		{
			List<ObjectCtrlInfo> list = AutoTrackObject.FindTargetCharaAndObjects(true, true, true, true, includeSelf, from);
			list.Sort(delegate(ObjectCtrlInfo c1, ObjectCtrlInfo c2)
			{
				float sqrMagnitude = (c1.guideObject.transformTarget.position - from.guideObject.transformTarget.position).sqrMagnitude;
				float sqrMagnitude2 = (c2.guideObject.transformTarget.position - from.guideObject.transformTarget.position).sqrMagnitude;
				if (sqrMagnitude < sqrMagnitude2) return -1;
				if (sqrMagnitude == sqrMagnitude2) return 0;
				return 1;
			});
			return list;
		}
		public void FindAndAttachNearestObjectCtrlInfo()
		{
			Studio.Studio instance = Singleton<Studio.Studio>.Instance;
			if (instance == null) return;
			float num = float.MaxValue;
			ObjectCtrlInfo objectCtrlInfo = null;
			foreach (ObjectCtrlInfo objectCtrlInfo2 in instance.dicObjectCtrl.Values)
			{
				if (objectCtrlInfo2 != this.sourceChara && objectCtrlInfo2.guideObject != null)
				{
					float sqrMagnitude = (objectCtrlInfo2.guideObject.transformTarget.position - this.sourceTransform.position).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						objectCtrlInfo = objectCtrlInfo2;
					}
				}
			}
			if (objectCtrlInfo != null && objectCtrlInfo != this.targetChara)
			{
				this.AttachTo(objectCtrlInfo, objectCtrlInfo.guideObject.transformTarget, false);
			}
		}

		public List<Transform> FindBonesFor(ObjectCtrlInfo studioChara)
		{
			List<Transform> bones = new List<Transform>();
			Action<ChaControl> action = delegate(ChaControl chara)
			{
				for (int i = 0; i < AutoTrackObject.keys.Length; i++)
				{
					GameObject referenceInfo = chara.GetReferenceInfo(AutoTrackObject.keys[i]);
					if (referenceInfo != null)
					{
						bones.Add(referenceInfo.transform);
					}
				}
			};
			if (studioChara is OCIChar)
			{
				ChaControl charInfo = (studioChara as OCIChar).charInfo;
				action(charInfo);
			}
			else if (studioChara is OCIItem)
			{
				bones.Add(studioChara.guideObject.transformTarget);
			}
			else if (studioChara is OCIFolder)
			{
				bones.Add(studioChara.guideObject.transformTarget);
			}
			return bones;
		}

		public void FindAndAttachNearestBone(bool findMale, bool findFemale, bool findItem, bool visibleOnly, bool includeSelfChara)
		{
			List<ObjectCtrlInfo> list = AutoTrackObject.FindTargetCharaAndObjects(findMale, findFemale, findItem, visibleOnly, includeSelfChara, this.sourceChara);
			float num = float.MaxValue;
			Transform transform = null;
			ObjectCtrlInfo objectCtrlInfo = null;
			foreach (ObjectCtrlInfo objectCtrlInfo2 in list)
			{
				List<Transform> list2 = this.FindBonesFor(objectCtrlInfo2);
				if (includeSelfChara)
				{
					Transform parent = this.sourceTransform;
					while (parent != null)
					{
						list2.Remove(parent);
						parent = parent.parent;
					}
				}
				for (int i = 0; i < list2.Count<Transform>(); i++)
				{
					Transform transform2 = list2[i];
					float sqrMagnitude = (transform2.position - this.sourceTransform.position).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						transform = transform2;
						objectCtrlInfo = objectCtrlInfo2;
					}
				}
			}
			if (transform != null && transform != this.targetTransform)
			{
				this.AttachTo(objectCtrlInfo, transform, false);
			}
		}
		public static List<ObjectCtrlInfo> FindTargetCharaAndObjects(bool findMale, bool findFemale, bool findItem, bool visibleOnly, bool includeSelfChara, ObjectCtrlInfo sourceChara)
		{
			Studio.Studio instance = Singleton<Studio.Studio>.Instance;
			if (instance == null) return new List<ObjectCtrlInfo>();
			List<ObjectCtrlInfo> list = new List<ObjectCtrlInfo>();
			foreach (ObjectCtrlInfo objectCtrlInfo in instance.dicObjectCtrl.Values)
			{
				if (!visibleOnly || AutoTrackObject.IsVisible(objectCtrlInfo))
				{
					if (findMale && objectCtrlInfo is OCIChar && (objectCtrlInfo as OCIChar).charInfo.sex == 0)
						list.Add(objectCtrlInfo);
					if (findFemale && objectCtrlInfo is OCIChar && (objectCtrlInfo as OCIChar).charInfo.sex == 1)
						list.Add(objectCtrlInfo);
					if (findItem && (objectCtrlInfo is OCIItem || objectCtrlInfo is OCIFolder))
						list.Add(objectCtrlInfo);
				}
			}
			if (!includeSelfChara)
			{
				list.Remove(sourceChara);
			}
			return list;
		}

		public static bool IsVisible(ObjectCtrlInfo chara)
		{
			bool result = false;
			if (chara is OCIItem || chara is OCIFolder) result = chara.objectInfo.visible;
			else if (chara is OCIChar) result = chara.objectInfo.visible;
			return result;
		}

		private void CalcOffset()
		{
			if (this.targetTransform == null)
			{
				this.offset = Vector3.zero;
				this.offsetRot = Quaternion.identity;
				return;
			}
			if (this.trackAsRelative)
			{
				this.offset = this.targetTransform.InverseTransformPoint(this.sourceTransform.position);
				this.offsetRot = Quaternion.Inverse(this.targetTransform.rotation) * this.sourceTransform.rotation;
				return;
			}
			this.offset = this.sourceTransform.position - this.targetTransform.position;
			this.offsetRot = Quaternion.Inverse(this.targetTransform.rotation) * this.sourceTransform.rotation;
		}

		public void SetZeroOffset()
		{
			this.offset = Vector3.zero;
			this.offsetRot = Quaternion.identity;
		}

		public void UpdatePosition()
		{
			if (this.targetTransform == null || this.sourceTransform == null) return;
			if (this.trackEnabled && !this.adjusting)
			{
				if (this.trackAsRelative)
				{
					this.sourceTransform.position = this.targetTransform.TransformPoint(this.offset);
					if (this.trackRoation)
					{
						this.sourceTransform.rotation = this.targetTransform.rotation * this.offsetRot;
					}
				}
				else
				{
					this.sourceTransform.position = this.targetTransform.position + this.offset;
					if (this.trackRoation)
					{
						this.sourceTransform.rotation = this.targetTransform.rotation * this.offsetRot;
					}
				}
			}
			if (this.adjusting)
			{
				this.CalcOffset();
				if (this.attachedAtFrame)
				{
					this.attachedAtFrame = false;
					this.adjusting = false;
				}
			}
		}
		private void OnDestroy()
		{
			AutoTrackObjectController.Get(this.sourceChara).autoTrackObjects.Remove(this);
		}

		private static CharReference.RefObjKey[] keys = new CharReference.RefObjKey[]
		{
			(CharReference.RefObjKey)2,
				(CharReference.RefObjKey)6,
				(CharReference.RefObjKey)8,
				(CharReference.RefObjKey)9,
				(CharReference.RefObjKey)10,
				(CharReference.RefObjKey)11,
				(CharReference.RefObjKey)7,
				(CharReference.RefObjKey)12,
				(CharReference.RefObjKey)13,
				(CharReference.RefObjKey)20,
				(CharReference.RefObjKey)21,
				(CharReference.RefObjKey)22,
				(CharReference.RefObjKey)23,
				(CharReference.RefObjKey)26,
				(CharReference.RefObjKey)27,
				(CharReference.RefObjKey)28,
				(CharReference.RefObjKey)29,
				(CharReference.RefObjKey)30,
				(CharReference.RefObjKey)142,
				(CharReference.RefObjKey)32,
				(CharReference.RefObjKey)33
		};

		public class AutoTrackObjectController : MonoBehaviour
		{
			public List<AutoTrackObject> autoTrackObjects = new List<AutoTrackObject>();
			public static AutoTrackObjectController Get(ObjectCtrlInfo studioChara)
			{
				AutoTrackObject.AutoTrackObjectController autoTrackObjectController = studioChara.guideObject.transformTarget.gameObject.GetComponent<AutoTrackObject.AutoTrackObjectController>();
				if (autoTrackObjectController == null)
				{
					autoTrackObjectController = studioChara.guideObject.transformTarget.gameObject.AddComponent<AutoTrackObject.AutoTrackObjectController>();
					autoTrackObjectController.StartTrack();
				}
				return autoTrackObjectController;
			}
			private void StartTrack()
			{
				base.StartCoroutine(this.AutoTrackedObjectPositionEndOfFrameCo());
			}

			private IEnumerator AutoTrackedObjectPositionEndOfFrameCo()
			{
				for (;;)
				{
					yield return new WaitForEndOfFrame();
					using (List<AutoTrackObject>.Enumerator enumerator = this.autoTrackObjects.GetEnumerator())
					{
						while (enumerator.MoveNext())
						{
							AutoTrackObject autoTrackObject = enumerator.Current;
							try
							{
								if (autoTrackObject.updateBeforeNextFrame)
								{
									autoTrackObject.UpdatePosition();
								}
							}
							catch (Exception value)
							{
								Console.WriteLine(value);
							}
						}
						continue;
					}
					//yield break;
				}
			}
			public void LateUpdate()
			{
				AutoTrackObjectMgr.Instance.DoLateUpdate(this);
			}
			public void DoLateUpdate()
			{
				foreach (AutoTrackObject autoTrackObject in this.autoTrackObjects)
				{
					try
					{
						if (!autoTrackObject.updateBeforeNextFrame)
						{
							autoTrackObject.UpdatePosition();
						}
					}
					catch (Exception value)
					{
						Console.WriteLine(value);
					}
				}
			}
	
		}
	}
}
